package com.qclix.inv;

import java.util.*;

import static java.util.Calendar.DAY_OF_MONTH;
import static java.util.Calendar.SATURDAY;
import static java.util.Calendar.SUNDAY;

/**
 * Provides timesheet related (utility) methods
 */
public abstract class TimesheetHelper {
    /**
     * Adds the working days to a timesheet so that it starts on the last sunday
     * preceding its invoicing starting date and finishes on the first saturday after
     * its invoicing period.
     *
     * @param timesheet the timesheet the days must be added to
     */
    public static void addNonPartOfInvoiceDays(Timesheet timesheet) {
        List<WeekDay> weekdayList = new ArrayList<WeekDay>();
        weekdayList.addAll(timesheet.getDays());
        Collections.sort(weekdayList);
        Date firstDayToDisplay = CalendarHelper.getStartDayOfWeekBefore(weekdayList.get(0).getDate(), Calendar.SUNDAY);
        Calendar cal = Calendar.getInstance();
        cal.setTime(firstDayToDisplay);
        while (cal.getTime().before(weekdayList.get(0).getDate())) {
            timesheet.getDays().add(createWeekDay(cal.getTime(), false));
            cal.add(Calendar.DAY_OF_YEAR, 1);
        }

        Date lastDayTDisplay = CalendarHelper.getFirstSaturdayAfter(weekdayList.get(weekdayList.size() - 1).getDate());
        cal = Calendar.getInstance();
        cal.setTime(weekdayList.get(weekdayList.size() - 1).getDate());
        cal.add(Calendar.DAY_OF_YEAR, 1);
        while (! cal.getTime().after(lastDayTDisplay)) {
            timesheet.getDays().add(createWeekDay(cal.getTime(), false));
            cal.add(Calendar.DAY_OF_YEAR, 1);
        }
    }

    /**
     * Modifies the days in the timesheet to set them to holidays when there date matches one of the holidays list
     * @param timesheet the timesheet to modify
     * @param holidays the list of Holidays
     * @throws NullPointerException if the timesheet is null
     */
    public static void setHolidays(Timesheet timesheet, List<Holiday> holidays) {
        // TODO avoid cartesian product
        if (holidays == null) {
            return;
        }
        
        for (WeekDay weekDay: timesheet.getDays()) {
            for (Holiday holiday: holidays) {
                if (holiday.isSameDay(weekDay.getDate())) {
                    weekDay.setHoliday(true);
                    break;
                }
            }
        }
    }

    public static Timesheet buildTimesheet(int month, int year) {
        Timesheet timesheet = new Timesheet();
        timesheet.setDays(getMonthDays(month, year));
        addNonPartOfInvoiceDays(timesheet);
        return timesheet;
    }

    public static SortedSet<WeekDay> getMonthDays(int month, int year) {
        Date firstDayOfMonth = getDate(1, month, year, false);

        Calendar cal = Calendar.getInstance();
        cal.setTime(firstDayOfMonth);
        cal.set(DAY_OF_MONTH, cal.getActualMaximum(DAY_OF_MONTH));
        Date lastDayOfMonth = cal.getTime();

        SortedSet<Date> dates = CalendarHelper.getDates(firstDayOfMonth, lastDayOfMonth);
        SortedSet<WeekDay> weekDays = new TreeSet<WeekDay>();

        for (Date date: dates) {
            weekDays.add(createWeekDay(date, true));
        }
        return weekDays;
    }
    
    private static Date getDate(int day, int month, int year, boolean endOfDay) {
        Calendar cal = Calendar.getInstance();
        cal.set(year, month, day);
        if (endOfDay) {
            cal.set(Calendar.HOUR_OF_DAY, 23);
            cal.set(Calendar.MINUTE, 59);
            cal.set(Calendar.SECOND, 59);
        }
        return cal.getTime();
    }

    private static WeekDay createWeekDay(Date date, boolean partOfInvoice) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        WeekDay weekDay = new WeekDay();
        weekDay.setDate(date);
        weekDay.setPartOfInvoice(partOfInvoice);
        weekDay.setWeekend(cal.get(Calendar.DAY_OF_WEEK) == SATURDAY || cal.get(Calendar.DAY_OF_WEEK) == SUNDAY);
        return weekDay;
    }
}
